// ============================================================================
// $Id: TestingFramework.hh,v 1.1.1.1 2004-09-06 12:37:50 botsch Exp $
// ----------------------------------------------------------------------------
#ifndef TESTINGFRAMEWORK_HH
#define TESTINGFRAMEWORK_HH
// ----------------------------------------------------------------------------

/** \file TestingFramework.hh
This file contains a little framework for test programms
*/


// ----------------------------------------------------------------------------

#include "Config.hh"
#include <iosfwd>
#include <sstream>
#include <vector>
#include <algorithm>
#include <stdexcept>
#include <Core/Utils/Noncopyable.hh>

// ------------------------------------------------------------- namespace ----

namespace OpenMesh { // BEGIN_NS_OPENMESH
	namespace Utils { // BEGIN_NS_UTILS


		// ----------------------------------------------------------------- class ----
		//
		// Usage Example
		//
		// #include <iostream>
		// #include <.../TestingFramework.hh>
		//
		// struct test_func : public TestingFramework::TestFunc
		// {
		//    typedef test_func Self;
		//
		//    // define ctor and copy-ctor
		//    test_func( TestingFramework& _th, std::string _n ) : TestingFramework::TestFunc( _th, _n ) { }
		//    test_func( Self& _cpy ) : TestingFramework::TestFunc(_cpy) { }
		//
		//    // overload body()
		//    void body()
		//    {
		//
		//       // Do the tests
		//       // direct call to verify
		//       verify( testResult, expectedResult, "additional information" );
		//
		//       // or use the define TH_VERIFY. The test-expression will be used as the message string
		//       TH_VERIFY( testResult, expectedResult );
		//
		//       ...
		//    }
		// };
		//
		// int main(...)
		// {
		//    TestingFramework testSuite(std::cout); // send output to stdout
		//
		//    new test_func(testSuite);        // create new test instance. It registers with testSuite.
		//    return testSuite.run();
		// }
		//

		// 
#define TH_VERIFY( expr, expt ) \
	verify( expr, expt, #expr )

		//
#define TH_VERIFY_X( expr, expt ) \
	verify_x( expr, expt, #expr )

		/** Helper class for test programms.
		\internal
		*/
		class TestingFramework : Noncopyable
		{
		public:

			typedef TestingFramework Self;
			typedef std::logic_error verify_error;

#ifndef DOXY_IGNORE_THIS
			class TestFunc
			{
			public:
				TestFunc( TestingFramework& _th, const std::string& _n ) 
					: th_(_th), name_(_n)
				{
					th_.reg(this);
				}

				virtual ~TestFunc()
				{ }

				void operator() ( void )
				{
					prolog();
					try
					{
						body();
					}
					catch( std::exception& x )
					{
						std::cerr << "<<Error>>: Cannot proceed test due to failure of last"
							<< "  test: " << x.what() << std::endl;
					}
					catch(...)
					{
						std::cerr << "Fatal: cannot proceed test due to unknown error!" 
							<< std::endl;
					}
					epilog();
				}

				const TestingFramework& testHelper() const { return th_; }

			protected:

				virtual void prolog(void)
				{
					begin(name_);
				}

				virtual void body(void) = 0;

				virtual void epilog(void)
				{
					end();
				}

			protected:

				TestingFramework& testHelper() { return th_; }

				TestFunc( const TestFunc& _cpy ) : th_(_cpy.th_), name_(_cpy.name_) { }


				// Use the following method in prolog()
				TestFunc& begin(std::string _title, const std::string& _info = "")
				{ th_.begin(_title,_info); return *this; }


				// Use the following method in epilog()
				TestFunc& end(void)
				{ th_.end(); return *this; }


				// Use the followin methods in body()

				template <typename ValueType>
				bool
					verify( const ValueType& _rc, const ValueType& _expected, 
					std::string _info )
				{ return th_.verify( _rc, _expected, _info ); }

				template <typename ValueType>
				void
					verify_x( const ValueType& _rc, const ValueType& _expected, 
					std::string _info )
				{
					if ( !verify(_rc, _expected, _info) )
						throw verify_error(_info);
				}

				TestFunc& info(const std::string& _info) 
				{ th_.info(_info); return *this; }

				TestFunc& info(const std::ostringstream& _ostr) 
				{ th_.info(_ostr); return *this; }

			private:      
				TestFunc();

			protected:
				TestingFramework& th_;
				std::string name_;      
			};
#endif

			typedef TestFunc*                TestFuncPtr;
			typedef std::vector<TestFuncPtr> TestSet;

		public:

			TestingFramework(std::ostream& _os) 
				: errTotal_(0),    errCount_(0),
				verifyTotal_(0), verifyCount_(0),
				testTotal_(0),   testCount_(0),
				os_(_os)
			{ }

		protected:

#ifndef DOXY_IGNORE_THIS
			struct TestDeleter
			{
				void operator() (TestFuncPtr _tfptr) { delete _tfptr; }
			};
#endif

		public:   

			virtual ~TestingFramework()
			{
				std::for_each(tests_.begin(), tests_.end(), TestDeleter() );
			}

		public:

			template <typename ValueType>
			bool verify(const ValueType& _rc, 
				const ValueType& _expected, 
				const std::string& _info)
			{
				++verifyTotal_;
				if ( _rc == _expected )
				{
					os_ << "    " << _info << ", result: " << _rc << ", OK!" << std::endl;
					return true;
				}
				++errTotal_;
				os_ << "    " << _info << ", result: " << _rc << " != " << _expected
					<< " <<ERROR>>" << std::endl;
				return false;
			}

			Self& begin(std::string _title, const std::string& _info = "")
			{
				std::ostringstream ostr;

				testTitle_ = _title;
				errCount_  = errTotal_;
				++testTotal_;

				ostr << _title;
				if ( !_info.empty() )
					ostr << " ["<< _info << "]";
				testTitle_ = ostr.str();
				os_ << "Begin " << testTitle_ << std::endl;
				return *this;
			}

			Self& end()
			{
				if (errorCount()==0)
					++testCount_;

				os_ << "End " << testTitle_ << ": " << errorCount() << " Error(s)." << std::endl;
				return *this;
			}

			Self& info(const std::string& _info)
			{
				os_ << "  + " << _info << std::endl;
				return *this;
			}

			Self& info(const std::ostringstream& _ostr)
			{
				os_ << "  + " << _ostr.str() << std::endl;
				return *this;
			}

			size_t errorTotal()  const { return errTotal_; }
			size_t errorCount()  const { return errTotal_ - errCount_; }
			size_t verifyTotal() const { return verifyTotal_; }
			size_t verifyCount() const { return verifyTotal_ - verifyCount_; }
			size_t goodTotal()   const { return verifyTotal() - errorTotal(); }
			size_t goodCount()   const { return verifyCount() - errorCount(); }

			size_t testTotal()   const { return testTotal_; }
			size_t testCount()   const { return testCount_; }

		public:

			int run(void)
			{
				os_ << "Test started\n";
				TestRunner executer;
				std::for_each(tests_.begin(), tests_.end(), executer );
				os_ << std::endl;
				os_ << "All tests completed" << std::endl
					<< "   #Tests: " << testCount() << "/" << testTotal() << std::endl
					<< "  #Errors: " << errorTotal() << "/" << verifyTotal() << std::endl;
				return errorTotal();
			}

		protected:

#ifndef DOXY_IGNORE_THIS
			struct TestRunner
			{
				void operator() (TestFuncPtr _tfptr) { (*_tfptr)(); }
			};   
#endif

			int reg(TestFuncPtr _tfptr)
			{
				tests_.push_back(_tfptr);
				return true;
			}

			friend class TestFunc;

		private:

			size_t errTotal_;    
			size_t errCount_;    
			size_t verifyTotal_;
			size_t verifyCount_;   
			size_t testTotal_;   // #Tests
			size_t testCount_;   // #Tests ohne Fehler

			std::string testTitle_;
			std::ostream& os_;

			TestSet tests_;

		};

		// ============================================================================
	} // END_NS_UTILS
} // END_NS_OPENMESH
// ============================================================================
#endif // TESTINGFRMEWORK_HH
// ============================================================================
